package org.mj.hz.proxy;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.mj.hz.config.MjRequestConfiguration;
import org.mj.hz.exception.MjRuntimeException;
import org.mj.hz.http.MjMethod;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class InterfaceProxyHandler<T> implements InvocationHandler {

    private  Class<T> interfaceClass;

    static Log log = LogFactory.get();

    private Map<Method, MjMethod> mjMethodMap = new HashMap<Method, MjMethod>();
    public InterfaceProxyHandler(Class<T> interfaceClass) {
        this.interfaceClass = interfaceClass;
        initMethods();
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MjMethod mjMethod = mjMethodMap.get(method);
        Object res = null;
        try {
            res = mjMethod.invoke(args);
        }catch (Exception e){ // 失败异常走rollback
            Object rollbackObject = mjMethod.getRollbackObject();
            log.warn(interfaceClass.getSimpleName() +"中的" +method.getName()+"方法降级！");
            log.error(e);
            if (ObjectUtil.isNotNull(rollbackObject)){
                if (! interfaceClass.isAssignableFrom(rollbackObject.getClass())){
                    throw new MjRuntimeException("服务降级类必须是服务接口的子类！");
                }
                res=  method.invoke(rollbackObject,args);
            }else{
                throw new MjRuntimeException(e);
            }
            return res;
        }
        return  res ;
    }

    private void initMethods() {
        Method[] methods = interfaceClass.getDeclaredMethods();
        MjRequestConfiguration mjRequestConfiguration = new MjRequestConfiguration();
        for(int i = 0; i < methods.length; i++) {
            Method method = methods[i];
            MjMethod mjMethod = null;
            try {
                mjMethod = new MjMethod(this, method,interfaceClass,mjRequestConfiguration);
            } catch (MjRuntimeException e) {
                throw new RuntimeException(e);
            }
            mjMethodMap.put(method, mjMethod);
        }
    }
}
